package xyz.yamdestiny.logparser.runner;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import xyz.yamdestiny.logparser.entity.ALog;
import xyz.yamdestiny.logparser.entity.Entrance;
import xyz.yamdestiny.logparser.entity.Node;
import xyz.yamdestiny.logparser.util.SpecialOutput;

@Component
public class MyApplicationRunner implements ApplicationRunner {

    private static final String NORMAL_SPACE = "    ";
    private static final String SPECIAL_SPACE = "        ";
    private static final String NODE_SPACE = "|   ";

    private static final String PARENT_NODE = "`---ts=";
    private static final String ENTRANCE_NODE = "`---[";
    private static final String NODE = "+---[";

    private static final String EXCEPTION_KEY = "[throws Exception]";
    private static final String THROW_KEY = "throw:";

    private static final String PATH = MyApplicationRunner.class.getResource("/trace-logs").getPath();

    private final Logger logger = LoggerFactory.getLogger(MyApplicationRunner.class);

    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<ALog> logs = parserLogFiles();

        logger.info("Log size: {}", logs.size());

        SpecialOutput output = new SpecialOutput();

        output.printLog(logs);
    }

    private List<ALog> parserLogFiles() throws UnsupportedEncodingException, FileNotFoundException, IOException {
        List<File> files = getFiles(PATH);

        List<ALog> logs = new ArrayList<ALog>();
        for (File file : files) {
            InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(reader);

            ALog logFile = null;

            String line = null;
            String content = "";
            while ((line = bufferedReader.readLine()) != null) {
                content += line + "\n";

                if (line.startsWith(PARENT_NODE)) {
                    // start with "`---ts="
                    logFile = new ALog();
                    logs.add(logFile);

                    logFile.setFileName(file.getName());
                    logFile.setFilePath(file.getPath());
                    logFile.setParentFilePath(file.getParent());

                    String[] lineArray = line.split(";");

                    logFile.setDate(lineArray[0].substring(PARENT_NODE.length()));
                    logFile.setThreadName(lineArray[1].substring("thread_name=".length()));
                    logFile.setId(lineArray[2].substring("id=".length()));
                    logFile.setDeamonFlag(lineArray[3].substring("is_daemon=".length()));
                    logFile.setPriority(lineArray[4].substring("priority=".length()));
                    logFile.setTccl(lineArray[5].substring("TCCL=".length()));
                }

                if (logFile == null) {
                    continue;
                }

                if (line.startsWith(NORMAL_SPACE + ENTRANCE_NODE)) {
                    String[] lineArray = line.trim().split(" ");

                    logFile.setEntrance(buildEntrance(lineArray));
                }

                String nodeName = SPECIAL_SPACE + NODE;
                if (line.startsWith(nodeName)) {
                    String[] lineArray = line.substring(line.lastIndexOf("|") + 1).trim().split(" ");

                    logFile.getNodes().add(buildNode(lineArray));
                }

                String subNodeName = SPECIAL_SPACE + NODE_SPACE;
                if (line.startsWith(subNodeName)) {
                    String[] lineArray = line.substring(line.lastIndexOf("|") + 1).trim().split(" ");

                    if (line.contains(EXCEPTION_KEY) || line.contains(THROW_KEY)) {

                    } else if (lineArray.length <= 2) {
                        logFile.getSubEntrance().add(buildEntrance(lineArray));
                    } else {
                        logFile.getNodes().add(buildNode(lineArray));
                    }
                }

                if (line == null || line.length() == 0) {
                    logFile.setContent(content);
                    content = "";
                }

            }
            reader.close();

        }
        return logs;
    }

    private Entrance buildEntrance(String[] lineArray) {
        Entrance entrance = new Entrance();

        entrance.setName(lineArray[1]);
        entrance.setTime(new Double(lineArray[0].substring(ENTRANCE_NODE.length(), lineArray[0].length() - 3)));

        return entrance;
    }

    private Node buildNode(String[] lineArray) {
        Node node = new Node();

        node.setName(lineArray[1]);

        String[] time = lineArray[0].split(",");

        if (time != null && time.length >= 4) {
            node.setTime(new Double(time[2].substring("total=".length(), time[2].length() - 2)));
            node.setCount(new Integer(time[3].substring("count=".length(), time[3].length() - 1)));
        } else {
            node.setTime(new Double(lineArray[0].substring(NODE.length(), lineArray[0].length() - 3)));
        }

        if (lineArray.length == 3) {
            node.setLineNumber(new Integer(lineArray[2].substring(1)));
        }

        return node;
    }

    private List<File> getFiles(String path) {
        File file = new File(path);
        File[] tempList = file.listFiles();

        if (tempList == null || tempList.length == 0) {
            throw new IllegalArgumentException("No files found.");
        }

        List<File> files = new ArrayList<>();
        for (File element : tempList) {
            if (element.isHidden()) {
                continue;
            }
            if (element.isFile()) {
                files.add(element);
            }
            if (element.isDirectory()) {
                files.addAll(getFiles(element.getPath()));
            }
        }
        return files;
    }

}
